﻿/**
 * MOTOR 2 - RAPID FLASH DYNAMICS
 * Copyright (c) 2006-2008 Michael Baczynski http://www.polygonal.de
 * 
 * Based on Box2D by Erin Catto, http://www.box2d.org
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 * 
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 * 
 * 1. The origin of this software must not be misrepresented; you must not
 * claim that you wrote the original software. If you use this software
 * in a product, an acknowledgment in the product documentation would be
 * appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 * misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 */
package de.polygonal.motor2.dynamics.contact {
	import de.polygonal.motor2.dynamics.ContactEvent;	
	
	import flash.utils.Dictionary;

	import de.polygonal.motor2.World;
	import de.polygonal.motor2.collision.pairwise.Collider;
	import de.polygonal.motor2.collision.shapes.ShapeSkeleton;
	import de.polygonal.motor2.collision.shapes.ShapeTypes;
	import de.polygonal.motor2.dynamics.RigidBody;
	import de.polygonal.motor2.dynamics.contact.generator.*;

	//import de.polygonal.ds.ArrayedQueue;
	
	//TODO refactor class
	public class Contact {

		public static var manifoldCache:Array;
		public static const k_bitDestroy:int = 0x01;
		public static const k_bitIsland:int = 0x20;
		protected var _collider:Collider;
		public var world:World;
		public var next:Contact;
		public var prev:Contact;
		public var node1:ContactNode;
		public var node2:ContactNode;
		public var shape1:ShapeSkeleton, body1:RigidBody;
		public var shape2:ShapeSkeleton, body2:RigidBody;
		public var manifolds:Array;
		public var manifoldCount:int;
		public var friction:Number;
		public var restitution:Number;
		//		public var islandFlag:Boolean;
		public var stateBits:int;
		protected var _doWarmStart:Boolean;
		public static var s_registers:Array;
		public static const s_registers2:Dictionary = new Dictionary(true);
		public static var s_initialized:Boolean = false;

		static public function resolve(shape1:ShapeSkeleton, shape2:ShapeSkeleton):Contact {
			return null;
		}

		static public function AddType(createFcn:*, destroyFcn:*, type1:int, type2:int):void {
			//			var id:int = (type1 << 16) | (type2 << 8);
			//			var c:ContactRegister = s_registers2[id] = new ContactRegister();
			//			c.createFcn = createFcn;
			//			c.destroyFcn = destroyFcn;
			//			c.primary = true;
			s_registers[type1][type2].createFcn = createFcn;
			s_registers[type1][type2].destroyFcn = destroyFcn;
			s_registers[type1][type2].primary = true;
			
			if (type1 != type2) {
				//				id = (type2 << 16) | (type1 << 8);
				//				c = s_registers2[id] = new ContactRegister();
				//				
				//				c.createFcn = createFcn;
				//				c.destroyFcn = destroyFcn;
				//				c.primary = false;
				s_registers[type2][type1].createFcn = createFcn;
				s_registers[type2][type1].destroyFcn = destroyFcn;
				s_registers[type2][type1].primary = false;
			}
		}

		static public function initialize():void {
			//TODO define k_maxContacts
			//manifoldCache = new ArrayedQueue(500);
			//for (var i:int = 0; i < 500; i++)
			//{
				//_bidQue = new ArrayedQueue(Constants.k_maxProxies);
				//for (var i:int = 0; i < Constants.k_maxProxies; i++) _bidQue.enqueue(i+1);
			//}
		}

		static public function InitializeRegisters():void {
			s_registers = new Array(ShapeTypes.SHAPE_COUNT);
			
			var k:int = ShapeTypes.SHAPE_COUNT;
			for (var i:int = 0;i < k; i++) {
				s_registers[i] = new Array(k);
				for (var j:int = 0;j < k; j++)
					s_registers[i][j] = new ContactRegister();
			}
			
			//TODO use id hashes, i think that.. destory function is not required
			AddType(CircleContact.create, CircleContact.destroy, ShapeTypes.CIRCLE, ShapeTypes.CIRCLE);
			AddType(BoxContact.create, BoxContact.destroy, ShapeTypes.BOX, ShapeTypes.BOX);
			AddType(PolyContact.create, PolyContact.destroy, ShapeTypes.POLY, ShapeTypes.POLY);
			AddType(PolyContact.create, PolyContact.destroy, ShapeTypes.BOX, ShapeTypes.POLY);
			AddType(BoxCircleContact.create, BoxCircleContact.destroy, ShapeTypes.BOX, ShapeTypes.CIRCLE);
			AddType(PolyCircleContact.create, PolyContact.destroy(), ShapeTypes.POLY, ShapeTypes.CIRCLE);
			
			//AddType(PolyContact.create , PolyContact.destroy, ShapeTypes.POLY, ShapeTypes.BOX);
			
			//AddType(b2PolyAndCircleContact.Create, b2PolyAndCircleContact.Destroy, b2Shape.e_polyShape, b2Shape.e_circleShape);
			//AddType(b2PolyContact.Create, b2PolyContact.Destroy, b2Shape.e_polyShape, b2Shape.e_polyShape);
		}

		static public function Destroy(contact:Contact):void {
			//b2Settings.b2Assert(s_initialized == true);
			if (contact.manifoldCount > 0) {
				contact.shape1.body.wakeUp();
				contact.shape2.body.wakeUp();
			}
			
			var type1:int = contact.shape1.type;
			var type2:int = contact.shape2.type;
			
			
			
			
			var destroyFcn:Function = s_registers[type1][type2].destroyFcn;
			destroyFcn(contact);
		}

		static public function Create(shape1:ShapeSkeleton, shape2:ShapeSkeleton):Contact {
			if (!s_initialized) {
				InitializeRegisters();
				s_initialized = true;
			}
			
			var type1:int = shape1.type;
			var type2:int = shape2.type;
		
			var createFcn:* = s_registers[type1][type2].createFcn;
			
			if (createFcn) {
				if (s_registers[type1][type2].primary) {
					return createFcn(shape1, shape2);
				}
				else {
					var c:Contact = createFcn(shape2, shape1);
					for (var i:int = 0;i < c.manifoldCount; ++i) {
						var m:Manifold = c.manifolds[i];
						m.nx = -m.nx;
						m.ny = -m.ny;
					}
					return c;
				}
			}
			else {
				return null;
			}
		}

		/*
		 * override in subclass if needed
		 */
		public function deconstruct():void {
			//TODO replace static function Destory
		}

		public static function createfast(s1:ShapeSkeleton, s2:ShapeSkeleton):Contact {
			var t1:int = s1.type;
			var t2:int = s2.type;
			
			//heterogenous
			if (t1 ^ t2) {
				if (t1 == ShapeTypes.CIRCLE) {
					if (t2 == ShapeTypes.BOX)
					{
						
					}
				}
				else
				if (t2 == ShapeTypes.BOX) {
					if (t1 == ShapeTypes.CIRCLE)
					{
						//create & flip
						/*
						var c:Contact = new CircleBoxContact();
						for (var i:int = 0; i < c.manifoldCount; i++)
						{
							var m:Manifold = c.getManifolds();
							m.normal.x = -m.normal.x;
							m.normal.y = -m.normal.y;
						}
						return c;*/
					}
				}
			}
			//homogeneous
			else {
				if (t1 == ShapeTypes.CIRCLE) {
					return new CircleContact(s1, s2);
				}
				else
				if (t1 == ShapeTypes.BOX)
				{
					
				}
			}
			
			/*
			b2ContactCreateFcn* createFcn = s_registers[type1][type2].createFcn;
			
			if (createFcn)
			{
			if (s_registers[type1][type2].primary)
			{
			return createFcn(shape1, shape2, allocator);
			}
			else
			{
			b2Contact* c = createFcn(shape2, shape1, allocator);
			for (int32 i = 0; i < c->GetManifoldCount(); ++i)
			{
			b2Manifold* m = c->GetManifolds() + i;
			m->normal = -m->normal;
			}
			return c;
			}
			}
			else
			{
			return NULL;
			}*/
			return null;
		}

		public function Contact(s1:ShapeSkeleton, s2:ShapeSkeleton) {
			if(!(this is NullContact)){
				s1.body.dispatchEvent(new ContactEvent(s2.type));
				s2.body.dispatchEvent(new ContactEvent(s1.type));
			}
			init(s1, s2);
		}

		public function init(s1:ShapeSkeleton, s2:ShapeSkeleton):void {
			shape1 = s1;
			shape2 = s2;
			
			_doWarmStart = World.doWarmStarting;
			
			manifoldCount = 0;
			manifolds = [];
			
			//values 0.100 
			friction = Math.sqrt(s1.friction * s2.friction);
			
			restitution = s1.restitution > s2.restitution ? s1.restitution : s2.restitution;
			
			//			islandFlag = false;
			
			//retreives world from shape.body.world
			world = s1.body.world;
			
			prev = null;
			next = null;
			
			node1 = new ContactNode();
			node2 = new ContactNode();
		}

		/**
		 * implement in subclass
		 */
		public function getManifolds():Array {
			return null;
		}

		public function evaluate():void {
		}

		public function getConstructor():Class {
			return null;
		}
	}
}

internal class ContactRegister {

	public var createFcn:Function;
	public var destroyFcn:Function;
	public var primary:Boolean;
}